﻿@page "/ellipticalArc"
@using VectSharp;
@using VectSharp.SVG;

<style>
	table {
		table-layout: fixed;
	}

	td {
		width: 200px;
		padding: 10px;
	}
</style>

<div style="width: 100vw; height: 100vh; position: relative;">
	<div style="width: calc(100% - 400px); height: 100%; position: absolute; top: 0; left: 0; text-align: center">
		<img src="@imgSource" style="max-width: 100%; max-height: 100%; margin-top: 50vh; transform: translate(0, -50%)" />
	</div>

	<div style="width: 400px; height: 100vh; position: absolute; top: 0; right: 0;">
		<table style="margin-top: 50vh; transform: translate(0, -50%)">
			<tr>
				<td>
					<MatNumericUpDownField Label="Radius X"
										   @bind-Value=@rX
										   Minimum="0"
										   Step="1">
					</MatNumericUpDownField>
				</td>
				<td>
					<MatNumericUpDownField Label="Radius Y"
										   @bind-Value=@rY
										   Minimum="0"
										   Step="1">
					</MatNumericUpDownField>
				</td>
			</tr>
			<tr>
				<td colspan="2" style="width: 400px; text-align: center">
					<MatNumericUpDownField Label="Axis angle"
										   @bind-Value=@phi
										   DecimalPlaces="2"
										   Step="0.01">
					</MatNumericUpDownField>
				</td>
			</tr>
			<tr>
				<td>
					<MatCheckbox @bind-Value="@largeArc">Large arc</MatCheckbox>
				</td>
				<td>
					<MatCheckbox @bind-Value="@clockwise">Clockwise</MatCheckbox>
				</td>
			</tr>

		</table>
	</div>
</div>

@code {
	private double _rX = 40;
	private double rX
	{
		get
		{
			return _rX;
		}

		set
		{
			_rX = value;
			Render();
		}
	}

	private double _rY = 35;
	private double rY
	{
		get
		{
			return _rY;
		}

		set
		{
			_rY = value;
			Render();
		}
	}

	private double _phi = 0;
	private double phi
	{
		get
		{
			return _phi;
		}

		set
		{
			_phi = value;
			Render();
		}
	}

	private bool _largeArc = false;
	private bool largeArc
	{
		get
		{
			return _largeArc;
		}

		set
		{
			_largeArc = value;
			Render();
		}
	}

	private bool _clockwise = false;
	private bool clockwise
	{

		get
		{
			return _clockwise;
		}

		set
		{
			_clockwise = value;
			Render();
		}
	}



	private string imgSource = "";

	protected override Task OnInitializedAsync()
	{
		Render();
		return Task.CompletedTask;
	}

	public void Render()
	{
		Page page = new Page(100, 100);
		Graphics graphics = page.Graphics;

		GraphicsPath path = new GraphicsPath().MoveTo(20, 30);

		path.EllipticalArc(rX, rY, phi, largeArc, clockwise, new Point(80, 70));

		RenderEllipse(graphics, true);
		RenderEllipse(graphics, false);

		graphics.FillPath(path, Colours.Green);
		graphics.StrokePath(path, Colours.Black, 2);

		using (MemoryStream ms = new MemoryStream())
		{
			page.SaveAsSVG(ms);
			ms.Seek(0, SeekOrigin.Begin);

			using (StreamReader sr = new StreamReader(ms))
			{
				this.imgSource = "data:image/svg+xml;base64," + Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(sr.ReadToEnd()));
			}
		}
	}

	private void RenderEllipse(Graphics gpr, bool sweepClockwise)
	{
		{
			double radiusX = rX;
			double radiusY = rY;
			double axisAngle = phi;

			if (radiusX == 0 || radiusY == 0)
			{
				return;
			}
			else
			{
				radiusX = Math.Abs(radiusX);
				radiusY = Math.Abs(radiusY);
			}

			double x1 = 20;
			double y1 = 30;

			double x2 = 80;
			double y2 = 70;

			double x1P = Math.Cos(axisAngle) * (x1 - x2) * 0.5 + Math.Sin(axisAngle) * (y1 - y2) * 0.5;

			if (Math.Abs(x1P) < 1e-7)
			{
				x1P = 0;
			}

			double y1P = -Math.Sin(axisAngle) * (x1 - x2) * 0.5 + Math.Cos(axisAngle) * (y1 - y2) * 0.5;

			if (Math.Abs(y1P) < 1e-7)
			{
				y1P = 0;
			}

			double lambda = x1P * x1P / (radiusX * radiusX) + y1P * y1P / (radiusY * radiusY);

			if (lambda > 1)
			{
				double sqrtLambda = Math.Sqrt(lambda);
				radiusX *= sqrtLambda;
				radiusY *= sqrtLambda;
			}

			double sqrtTerm = (largeArc != sweepClockwise ? 1 : -1) * Math.Sqrt(Math.Max(0, (radiusX * radiusX * radiusY * radiusY - radiusX * radiusX * y1P * y1P - radiusY * radiusY * x1P * x1P) / (radiusX * radiusX * y1P * y1P + radiusY * radiusY * x1P * x1P)));

			double cXP = sqrtTerm * radiusX * y1P / radiusY;
			double cYP = -sqrtTerm * radiusY * x1P / radiusX;

			double cX = Math.Cos(axisAngle) * cXP - Math.Sin(axisAngle) * cYP + (x1 + x2) * 0.5;
			double cY = Math.Sin(axisAngle) * cXP + Math.Cos(axisAngle) * cYP + (y1 + y2) * 0.5;

			double theta1 = AngleVectors(1, 0, (x1P - cXP) / radiusX, (y1P - cYP) / radiusY);
			double deltaTheta = AngleVectors((x1P - cXP) / radiusX, (y1P - cYP) / radiusY, (-x1P - cXP) / radiusX, (-y1P - cYP) / radiusY) % (2 * Math.PI);

			if (!sweepClockwise && deltaTheta > 0)
			{
				deltaTheta -= 2 * Math.PI;
			}
			else if (sweepClockwise && deltaTheta < 0)
			{
				deltaTheta += 2 * Math.PI;
			}

			double r = Math.Min(radiusX, radiusY);

			/*GraphicsPath pth = new GraphicsPath();

			pth.Arc(0, 0, r, 0, 2 * Math.PI);

			gpr.Save();
			gpr.Translate(cX, cY);
			gpr.Rotate(axisAngle);
			gpr.Scale(radiusX / r, radiusY / r);

			gpr.StrokePath(pth, Colour.FromRgb(180, 180, 180), lineDash: new LineDash(5, 5, 0));

			gpr.Restore();*/

			object arc = Activator.CreateInstance(typeof(GraphicsPath).Assembly.GetType("VectSharp.ArcSegment"), new object[] { 0.0, 0.0, r, 0.0, 2 * Math.PI });

			Type arcSegmentType = arc.GetType();

			System.Reflection.MethodInfo minfo = arc.GetType().GetMethod("ToBezierSegments");

			Segment[] segments = (Segment[])arc.GetType().GetMethod("ToBezierSegments").Invoke(arc, null);

			GraphicsPath pth = new GraphicsPath();

			for (int i = 0; i < segments.Length; i++)
			{
				for (int j = 0; j < segments[i].Points.Length; j++)
				{
					double newX = segments[i].Points[j].X * radiusX / r;
					double newY = segments[i].Points[j].Y * radiusY / r;

					segments[i].Points[j] = new Point(newX * Math.Cos(axisAngle) - newY * Math.Sin(axisAngle) + cX, newX * Math.Sin(axisAngle) + newY * Math.Cos(axisAngle) + cY);

					if (i == 0 && j == 0)
					{
						pth.MoveTo(segments[i].Points[j]);
					}
				}
			}

			pth.Segments.AddRange(segments);

			gpr.StrokePath(pth, Colour.FromRgb(180, 180, 180), lineDash: new LineDash(5, 5, 0));
		}
	}

	private static double AngleVectors(double uX, double uY, double vX, double vY)
	{
		double tbr = Math.Acos((uX * vX + uY * vY) / Math.Sqrt((uX * uX + uY * uY) * (vX * vX + vY * vY)));
		double sign = Math.Sign(uX * vY - uY * vX);
		if (sign != 0)
		{
			tbr *= sign;
		}
		return tbr;
	}
}
